home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Technology Seed / Mac Tech Seed Feb '97.toast / OpenDoc 1.2b2c1 / Implementation / SystemProcess / SysProc.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-13  |  41.0 KB  |  1,399 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        SysProc.cpp
  3.  
  4.     Contains:    OpenDoc™ System Process
  5.  
  6.     Owned by:    Nick Pilch
  7.  
  8.     Copyright:    © 1993 - 1997 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.         <21>      1/8/97    DH        1401434: Specifically check if document is
  13.                                     missing and report to the user in
  14.                                     HandleCloseReopenEvent;
  15.                                     Also don't return error back to AE Mgr,
  16.                                     since it has been reported already;
  17.                                     increase time to wait for document to close
  18.                                     before reopening it.
  19.         <20>      10/23/96    DH        Fixed another build break problem.
  20.         <19>      10/23/96    TJ        Made it compile.
  21.         <18>      10/23/96    DH        1336808    ,1333949,1368888,1375780 If doc
  22.                                     doesn't launch because of out-of-memory,
  23.                                     send event to OpenDoc to adjust memory
  24.                                     size.
  25.         <17>     10/5/96    NP        Fixed notification manager interactions.
  26.         <16>     10/5/96    NP        Debug build only messsages to heap with
  27.                                     debugging.
  28.         <15>    02.10.1996    NP        1391011: hard-coded strings
  29.         <14>    27.09.1996    NP        1332145: Bad message when launch fails
  30.         <13>    11.09.1996    NP        1386084: undo changes that tried to
  31.                                     accomodate old launcher
  32.         <12>     7/11/96    TJ        Fixed Name of function to check for MacOS 8
  33.         <11>    11.07.1996    NP        1365143: Can't find system process error
  34.                                     messages.
  35.         <10>     6/26/96    NP        10002: Launch time speedups.
  36.          <9>     6/23/96    NP        1361269: Wait until notification manager
  37.                                     tasks have completed before quitting.
  38.                                     1323565: Refuse to run under MacOS 8.
  39.          <8>     6/19/96    NP        10002: Launch time speedups.
  40.          <7>     6/18/96    NP        10002: Launch time speedups.
  41.          <6>     6/14/96    NP        10002: Launch time speedups
  42.          <5>    .05.1996    NP        10002: Launch time fixes.
  43.          <4>    .05.1996    NP        More implementation.
  44.          <3>    .05.1996    NP        Working on it.
  45.          <2>    .04.1996    NP        Check in comment about bug that might have
  46.                                     to be worked around.
  47.          <1>    .04.1996    NP        first checked in
  48.  
  49.     To Do:
  50.     
  51.     In Progress:
  52.         
  53. */
  54.  
  55.  
  56. #ifndef __LIMITS__
  57. #include <Limits.h>
  58. #endif
  59.  
  60. #ifndef __GESTALT__
  61. #include <Gestalt.h>
  62. #endif
  63.  
  64. #ifndef __TEXTUTILS__
  65. #include <TextUtils.h>
  66. #endif
  67.  
  68. #ifndef __SCRAP__
  69. #include <Scrap.h>
  70. #endif
  71.  
  72. #ifndef __TOOLUTILS__
  73. #include <ToolUtils.h>
  74. #endif
  75.  
  76. #ifndef __ALIASES__
  77. #include <Aliases.h>
  78. #endif
  79.  
  80.  
  81. #ifndef _PASCLSTR_
  82. #include "PasclStr.h"
  83. #endif
  84.  
  85. #ifndef _ODXFUNCS_
  86. #include "ODXFuncs.h"
  87. #endif
  88.  
  89. #ifndef _ODXDPFNS_
  90. #include "ODXDpFns.h"
  91. #endif
  92.  
  93. #ifndef _ODPRCS_
  94. #include "ODPrcs.h"
  95. #endif
  96.  
  97. #ifndef _ODDEBUG_
  98. #include "ODDebug.h"
  99. #endif
  100.  
  101. #ifndef _EXCEPT_
  102. #include "Except.h"
  103. #endif
  104.  
  105. #ifndef _ODMEMORY_
  106. #include "ODMemory.h"
  107. #endif
  108.  
  109. #ifndef _SHELLDEF_
  110. #include "ShellDef.h"
  111. #endif
  112.  
  113. #ifndef _PLFMFILE_
  114. #include "PlfmFile.h"
  115. #endif
  116.  
  117. #ifndef _TEMPOBJ_
  118. #include "TempObj.h"
  119. #endif
  120.  
  121. #ifndef _INFOUTIL_
  122. #include "InfoUtil.h"
  123. #endif
  124.  
  125. #ifndef _SEUTILS_
  126. #include "SEUtils.h"
  127. #endif
  128.  
  129. #ifndef _MEMDEBG_
  130. #include "MemDebg.h"
  131. #endif
  132.  
  133. #ifndef ODDebug
  134. #error "Expected \"ODDebug\" to be defined."
  135. #endif
  136.  
  137. //==============================================================================
  138. // Constants
  139. //==============================================================================
  140.  
  141. // This is ten seconds in ticks.
  142. #define kWaitNumTicks 10*60
  143. const unsigned long kWaitForDocClosedTicks = 60*100; // Time to wait for document
  144.                                              // to close when trying to reopen it.
  145.  
  146. //==============================================================================
  147. // Globals
  148. //==============================================================================
  149.  
  150. #ifndef __MWERKS__
  151. QDGlobals qd;
  152. #endif
  153.  
  154. //==============================================================================
  155. // Macros
  156. //==============================================================================
  157.  
  158. //#define CHECK(EXPR)    if((err=(EXPR)) == noErr) ; else return err
  159.  
  160. //==============================================================================
  161. // Function Prototypes
  162. //==============================================================================
  163.  
  164. static void                    MainEventLoop();
  165. static pascal OSErr            HandleQuit(const AppleEvent*, AppleEvent*, long);
  166. static long                    GetSleepTime();
  167. static void                    InstallEventHandler(AEEventClass theAEEventClass,
  168.                                     AEEventID                 theAEEventID,
  169.                                     AEEventHandlerProcPtr     handler);
  170. static AEEventHandlerUPP    CreateAEEventHandlerUPP(
  171.                                                 AEEventHandlerProcPtr handler);
  172. static void                    ReportErrorWithNum(StringPtr errorString,
  173.                                                 OSErr error);
  174. static void                    ReportError(StringPtr errorString);
  175. static pascal void            InstallNotification(StringPtr errorString);
  176. static ODBoolean            ShouldIQuit();
  177. static OSType                GetOurCreator();
  178. static void                    ChangeOurCreator(OSType);
  179. static OSErr                FindLauncher(FSSpec* fileSpec);
  180. static OSErr                ReenableLauncher();
  181. static OSErr                EnableApp(FSSpec* fileSpec);
  182. static OSErr                EnableAppIfNotAlready(FSSpec* fileSpec);
  183. static void                    DisableApp(FSSpec* fileSpec, OSType newSignature);
  184. static OSErr                DoCatSearchesAndEnable(OSType creator);
  185. static void                    DeleteOldLauncher();
  186. static pascal OSErr            HandleControlMessage(const AppleEvent*, AppleEvent*, long);
  187. static pascal OSErr            HandleCloseReopenEvent(const AppleEvent*, AppleEvent*, long);
  188. void                        CheckPrefs();
  189. static void                    CheckForCorrectName();
  190. static ODBoolean            SystemIsTooOld();
  191. #if !GENERATINGCFM
  192. ODBoolean                     CFMIsPresent();
  193. #endif
  194.  
  195.  
  196. //==============================================================================
  197. // Code
  198. //==============================================================================
  199.  
  200. //------------------------------------------------------------------------------
  201. // CFM Init routine, SysProcCFMInit
  202. //------------------------------------------------------------------------------
  203.  
  204. FSSpec    gFileSpec;
  205.  
  206. extern "C" pascal OSErr SysProcCFMInit(CFragInitBlockPtr initBlkPtr);
  207. pascal OSErr SysProcCFMInit(CFragInitBlockPtr initBlkPtr)
  208. {
  209.     OSErr    result = noErr;
  210.  
  211.     switch( initBlkPtr->fragLocator.where )
  212.     {
  213.         case kDataForkCFragLocator:
  214.         case kResourceCFragLocator:
  215.             gFileSpec = *(initBlkPtr->fragLocator.u.onDisk.fileSpec);
  216.             break;
  217.         default:
  218.             result = fragCorruptErr;
  219.             WARN("SysProcCFMInit: Not expecting this code fragment"
  220.                     " location %ld!", initBlkPtr->fragLocator.where);
  221.             break;
  222.     }
  223.     
  224.     return result;
  225. }
  226.  
  227. //------------------------------------------------------------------------------
  228. // main
  229. //------------------------------------------------------------------------------
  230.  
  231. // IF WE ARE LAUNCHED WITH CREATOR kODShellSignature, WE MUST NOT RUN. OTHERWISE
  232. //    WE ARE TRYING TO ACT AS THE LAUNCHER, BUT CAN NEVER GET OPEN DOCUMENT EVENTS
  233. ODBoolean    gShouldRun = kODTrue;
  234.  
  235. // MAKE SURE WE DON'T GO AWAY BEFORE ALL NOTIFICATION MANAGER TASKS ARE REMOVED
  236. long        gPendingNotifications = 0;
  237.  
  238. ODBoolean    gIsSystem753OrLater;
  239.  
  240. ODBoolean    gRunUntilQuitEvent = false;
  241.  
  242. Size    gSize = kDocStubDefaultSize;
  243.         
  244. void main(void)
  245. {
  246.     OSErr    error;
  247. /*
  248. Here's something I found in a technote (PS02):
  249.  
  250. Currently there is a bug with 'appe's that have an 'INIT' under System 7.5 with
  251. PowerTalk installed. The BOA's heap can be corrupted if MaxApplZone is called.
  252. The current workaround is to compare the low-memory global HeapEnd to the BOA's
  253. bkLim field of its zone (obtained by calling GetZone). If the two values aren't
  254. the same, set HeapEnd to the value contained in theZone->bkLim (use the low
  255. memory accessors LMGetHeapEnd and LMSetHeapEnd to access/modify the low memory
  256. global.) This should be the first thing done in your BOA code, before setting
  257. the stack size or calling MaxApplZone.
  258. */
  259.     MaxApplZone();
  260. //    MoreMasters();
  261.  
  262.     // The scrap is copied into our application heap when the process is launched,
  263.     // occuping space otherwise used for launching documents.
  264.     // If the scrap is just the right size (approx. 30K), there isn't enough
  265.     // heap space left to launch a single document (the launcher will bail with 
  266.     // error -108).  Worse, if the scrap is just the right size, around 40K, 
  267.     // InitWindows will hang the system.  To avoid this, always unload the scrap.
  268.     // Testing for a nil scrap handle seems to avoid a write to nil by UnloadScrap.
  269.     // [cc]
  270.     if ( InfoScrap()->scrapHandle != nil )
  271.         UnloadScrap();
  272.  
  273.     InitGraf((Ptr)&qd.thePort);
  274.  
  275. #if !GENERATINGCFM
  276.     if ( ! CFMIsPresent())
  277.         return;
  278. #endif
  279.  
  280.     // WARNING. MUST CALL InitODMemory AND ODInitExceptions BEFORE USING ANY
  281.     //    EXCEPTION AND MEMORY UTILITIES!!!!!
  282.     error = InitODMemory();
  283.     if (error)
  284.     {
  285.         Str255    errorString;
  286.  
  287.         GetIndString(errorString, kODSysProcStringsID,
  288.                         kODSPStrIndexNoMemoryToStartUp);
  289.         ReportErrorWithNum(errorString, error);
  290.         goto bail;
  291.     }
  292.     ODInitExceptions();        // Sets up SOMError and SOMPrintf
  293.  
  294.     if (SystemIsTooOld())
  295.         goto bail;
  296.  
  297.     // see how long we should stick around.
  298.     CheckPrefs();
  299.  
  300. #if ODDebug
  301.     if (IsOptionKeyDown())
  302.         WARN("Beginning of System Process main.");
  303. #endif
  304. #if 0 /* don't do this stuff anymore, but should check system version */
  305.     // CHECK TO SEE IF IT'S APPROPRIATE FOR US TO BE LAUNCHED.
  306.     //    DISABLE LAUNCHER FUNCTIONALITY IF WE ARE RUNNING ON OLD SYSTEMS.
  307.     OSType    creator = GetOurCreator();
  308.     gIsSystem753OrLater = IsSystem753OrGreater();
  309.     if (!gIsSystem753OrLater)
  310.     {
  311.         if (creator != kODSystemProcessSignature)
  312.         {
  313.             // ONLY IF WE CAN MAKE SURE A LAUNCHER IS ENABLED SHOULD WE DISABLE
  314.             //    OURSELVES
  315.             if (!ReenableLauncher())
  316.             {
  317.                 DisableApp(&gFileSpec, kODSystemProcessSignature);
  318.                 gShouldRun = kODFalse;
  319.             }
  320.         }
  321.     }
  322.     else
  323.     {
  324.         // ALWAYS CHECK TO SEE IF MY NAME IS CORRECT. IF NOT, RENAME THE
  325.         //    LAUNCHER, AND THEN RENAME OURSELVES.
  326.         CheckForCorrectName();
  327.         // THIS IS THE CASE I CARE MORE ABOUT. USER CAN ONLY UPGRADE A
  328.         //    PARTICULAR SYSTEM, NOT DOWNGRADE IT.
  329.         if (creator != kODShellSignature)
  330.             EnableApp(&gFileSpec);
  331.     }
  332. #endif /* 0 */
  333.     TRY
  334.         AEEventHandlerUPP    handlerUPP;
  335.     
  336.         InstallEventHandler(kCoreEventClass, kAEQuitApplication, HandleQuit);
  337.  
  338.         handlerUPP = CreateAEEventHandlerUPP(HandleRequiredEvent);
  339.         THROW_IF_ERROR(AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
  340.                                 handlerUPP, 0, false));
  341.         THROW_IF_ERROR(AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
  342.                                 handlerUPP, 0, false));
  343.  
  344.         handlerUPP = NewAEEventHandlerProc(HandleLaunchFailed);
  345.         THROW_IF_ERROR(AEInstallEventHandler(kCoreEventClass,
  346.                         kAEApplicationDied, handlerUPP, 0, false));
  347.         THROW_IF_ERROR(AEInstallEventHandler(kCFMLaunchFailedEventClass,
  348.                         kCFMLaunchFailedEventID, handlerUPP, 0, false));
  349.  
  350.         InstallEventHandler(kODShellSignature, kSysProcControlEventID,
  351.                             HandleControlMessage);
  352.         InstallEventHandler(kODShellSignature, kODReopenDocEventID,
  353.                             HandleCloseReopenEvent);
  354.     CATCH_ALL
  355.         WARN("COULDN'T EVEN INITIALIZE THE SYSTEM PROCESS!");
  356.         Str255    errorString;
  357.  
  358.         GetIndString(errorString, kODSysProcStringsID,
  359.                         kODSPStrIndexUnrecoverableStartUpError);
  360.         ReportErrorWithNum(errorString, error);
  361.         goto bail;
  362.     ENDTRY
  363.  
  364. //    ODSession*    session;
  365.  
  366. //    TRY
  367. //        session = new ODSession;
  368. //        THROW_IF_NULL(session);
  369. //        session->InitSession(somGetGlobalEnvironment());
  370. //    CATCH_ALL
  371. //        ReportErrorWithNum("Initialization of OpenDoc failed, dude.", ErrorCode());
  372. //        return;
  373. //    ENDTRY
  374.  
  375.     MainEventLoop();
  376.   
  377.   bail:
  378.   
  379.     EventRecord    anEvent;
  380.     while(gPendingNotifications)
  381.         WaitNextEvent(everyEvent, &anEvent, 0, (RgnHandle) nil);
  382. //    delete session;
  383. }
  384.  
  385. //------------------------------------------------------------------------------
  386. // CreateAEEventHandlerUPP
  387. //------------------------------------------------------------------------------
  388.  
  389. static AEEventHandlerUPP CreateAEEventHandlerUPP(AEEventHandlerProcPtr handler)
  390. {
  391.     AEEventHandlerUPP handlerUPP = NewAEEventHandlerProc(handler);
  392.     THROW_IF_NULL(handlerUPP);
  393.     return handlerUPP;
  394. }
  395.  
  396. //------------------------------------------------------------------------------
  397. // InstallEventHandler
  398. //------------------------------------------------------------------------------
  399.  
  400. static void InstallEventHandler(AEEventClass                theAEEventClass,
  401.                                     AEEventID                theAEEventID,
  402.                                     AEEventHandlerProcPtr    handler)
  403. {
  404.     AEEventHandlerUPP handlerUPP = CreateAEEventHandlerUPP(handler);
  405.     THROW_IF_ERROR(AEInstallEventHandler(theAEEventClass, theAEEventID,
  406.             handlerUPP, 0, false)); // no refcon, not a system handler
  407. }
  408.  
  409. //------------------------------------------------------------------------------
  410. // MainEventLoop
  411. //------------------------------------------------------------------------------
  412.  
  413. static Boolean    gShouldQuit = false;
  414.  
  415.  
  416. const ODULong    kSleepTime = 10 * 60; // time (in ticks) to wait before returning
  417.                                         //    from WaitNextEvent
  418. // OLD LAUNCHER BEHAVIOR
  419. const ODULong    kWaitForErrorDelay = 8 * 60; // time (in ticks) to wait for
  420.                                                 //    "couldn't launch" Apple
  421.                                                 //    event when we are acting
  422.                                                 //    like the old launcher.
  423. const ODULong    kOLQuitDelay = 1 * 60; //    time (in ticks) to wait for open
  424.                                         //    document event.
  425. static ODULong    gQuitTime;
  426.  
  427. // NEW LAUNCHER BEHAVIOR
  428. const ODULong    kNLQuitDelay = 5 * 60; // time (in ticks) to wait until quitting
  429.                                         //    when no OD documents are running.
  430.  
  431. static void MainEventLoop(void)
  432. {
  433.     EventRecord    anEvent;
  434.     OSErr        error;
  435.     Boolean        quitCountDownOn = kODFalse;
  436.     ODULong        countDownStartTime;
  437.  
  438.     gQuitTime = TickCount() + kOLQuitDelay;
  439.  
  440.     while( ! ShouldIQuit() )
  441.     {
  442.         TRY
  443.             WaitNextEvent(everyEvent, &anEvent, GetSleepTime(), (RgnHandle) nil);
  444.  
  445.             switch (anEvent.what)
  446.             {
  447.                 case kHighLevelEvent:
  448.                     error = AEProcessAppleEvent(&anEvent);
  449.     #if ODDebug
  450.                     if (error && (error != errAEEventNotHandled))
  451.                         ReportErrorWithNum("\pError processing Apple event.", error);
  452.     #endif
  453.                     // AFTER HANDLING HIGH LEVEL EVENT, QUIT IF IT'S APPROPRIATE
  454.                     if (!gShouldRun)
  455.                         gShouldQuit = kODTrue;
  456.                     break;
  457.                 
  458.                 default:
  459.                     break;
  460.             }
  461.             if (!gRunUntilQuitEvent)
  462.             {
  463.                 if (!AreODDocumentsRunning())
  464.                 {
  465.                     if (!quitCountDownOn)
  466.                     {
  467.                         quitCountDownOn = kODTrue;
  468.                         countDownStartTime = TickCount();
  469.                     }
  470.                     else
  471.                     {
  472.                         if (TickCount() - countDownStartTime > kNLQuitDelay)
  473.                             gShouldQuit = kODTrue;
  474.                     }
  475.                 }
  476.                 else
  477.                     quitCountDownOn = kODFalse;
  478.             }
  479.         CATCH_ALL
  480.             WARN("Throw in main event loop!", ErrorCode());
  481.         ENDTRY
  482.     }
  483.     // CLEAN UP. WE DO THIS FOR *EVERY* QUIT. BUT IT SHOULDN'T TAKE
  484.     //    NOTICABLE TIME.
  485. //    if (gIsSystem753OrLater)
  486. //        DeleteOldLauncher();
  487. }
  488.  
  489. //------------------------------------------------------------------------------
  490. //  ShouldIQuit
  491. //------------------------------------------------------------------------------
  492.  
  493. static ODBoolean ShouldIQuit()
  494. {
  495.     return gShouldQuit && (gPendingNotifications == 0);
  496. }
  497.  
  498. //------------------------------------------------------------------------------
  499. //  HandleQuit
  500. //------------------------------------------------------------------------------
  501.  
  502. static pascal OSErr HandleQuit( const AppleEvent*, AppleEvent*, long )
  503. {
  504.     gShouldQuit = true;
  505.  
  506.     return noErr;
  507. }
  508.  
  509. //------------------------------------------------------------------------------
  510. //  GetSleepTime
  511. //------------------------------------------------------------------------------
  512.  
  513. static long GetSleepTime()
  514. {
  515.     return kSleepTime; // arbitrary for now
  516. }
  517.  
  518. //------------------------------------------------------------------------------
  519. //  NotifyLaunchFailed
  520. //
  521. //    See also OpenDocX.cpp-NotifyLaunchFailed
  522. //
  523. //    We do ParamText-style substitutions here using the ^0, ^1, etc. sequences.
  524. //
  525. //    Note that the substitutions done here are 2 levels deep. Substitutions
  526. //    are done into the string found at kODSysProcStringsID, 1. The second
  527. //    substitution "^1" is done with a string found at kLaunchFailedErrStrings
  528. //    which may have additional (^2 and/or ^3) substitution sequences.
  529. //------------------------------------------------------------------------------
  530.  
  531. void NotifyLaunchFailed(OSErr errorCode, Str255 appName, Str255 libName)
  532. {
  533.     Str255    templateString;
  534.     Handle    templateStringH = NULL;
  535.     Str255    tempString;
  536.     Handle    tempStringH;
  537.     short    index;
  538.     short    ignoreValue;
  539.  
  540.     TRY
  541.         templateString[0] = 0;
  542.         tempString[0] = 0;
  543.         GetIndString(templateString, kODSysProcStringsID,
  544.                         kODSPStrIndexComplexErrMssg);
  545.         THROW_IF_ERROR(PtrToHand(templateString + 1, &templateStringH,
  546.                                     templateString[0]));
  547.         
  548.         THROW_IF_ERROR(PtrToHand(appName + 1, &tempStringH, appName[0]));
  549.         ignoreValue = ReplaceText(templateStringH, tempStringH, "\p^0");
  550.         DisposeHandle(tempStringH);
  551.  
  552.         if( errorCode <= -2800 && errorCode >= -2824 )
  553.             index = 2 - 2800 - errorCode;    // Maps -2800..-2824 -> 2..26
  554.         else
  555.             index = 1;
  556.         GetIndString(tempString, kLaunchFailedErrStrings, index);
  557.         THROW_IF_ERROR(PtrToHand(tempString + 1, &tempStringH,
  558.                                     tempString[0]));
  559.         ignoreValue = ReplaceText(templateStringH, tempStringH, "\p^1");
  560.         DisposeHandle(tempStringH);
  561.  
  562.         THROW_IF_ERROR(PtrToHand(libName + 1, &tempStringH, libName[0]));
  563.         ignoreValue = ReplaceText(templateStringH, tempStringH, "\p^2");
  564.         DisposeHandle(tempStringH);
  565.  
  566.         NumToString(errorCode, tempString);
  567.         THROW_IF_ERROR(PtrToHand(tempString + 1, &tempStringH,
  568.                                     tempString[0]));
  569.         ignoreValue = ReplaceText(templateStringH, tempStringH, "\p^3");
  570.         DisposeHandle(tempStringH);
  571.  
  572.         Size handleSize = GetHandleSize(templateStringH);
  573.         THROW_IF_ERROR(MemError());
  574.         if (handleSize > UCHAR_MAX)
  575.             handleSize = UCHAR_MAX;
  576.         templateString[0] = handleSize;
  577.         ODBlockMove(*templateStringH, templateString + 1, handleSize);
  578.         
  579.         ReportError(templateString);
  580.     CATCH_ALL
  581.     ENDTRY
  582.  
  583.     if (templateStringH)
  584.         DisposeHandle(templateStringH);
  585. }
  586.  
  587. //------------------------------------------------------------------------------
  588. //  ReportDocumentError
  589. //
  590. //    See NotifyLaunchFailed for implementation notes. Only a simple one stage
  591. //    substitution is done here.
  592. //------------------------------------------------------------------------------
  593.  
  594. void ReportDocumentError(StringPtr docName, OSErr err)
  595. {
  596.     Str255    templateString;
  597.     Handle    templateStringH = NULL;
  598.     Str255    tempString;
  599.     Handle    tempStringH;
  600.     short    ignoreValue;
  601.  
  602.     TRY
  603.         templateString[0] = 0;
  604.         tempString[0] = 0;
  605.  
  606.         short stringIndex = docName[0] ? kODSPStrIndexTwoPartErrMssg
  607.                                         : kODSPStrIndexOnePartErrMssg;
  608.  
  609.         GetIndString(templateString, kODSysProcStringsID,
  610.                         stringIndex);
  611.         THROW_IF_ERROR(PtrToHand(templateString + 1, &templateStringH,
  612.                                     templateString[0]));
  613.  
  614.         if (docName[0])
  615.         {
  616.             THROW_IF_ERROR(PtrToHand(docName + 1, &tempStringH, docName[0]));
  617.             ignoreValue = ReplaceText(templateStringH, tempStringH, "\p^0");
  618.             DisposeHandle(tempStringH);
  619.         }
  620.  
  621.         NumToString(err, tempString);
  622.         THROW_IF_ERROR(PtrToHand(tempString + 1, &tempStringH, tempString[0]));
  623.         ignoreValue = ReplaceText(templateStringH, tempStringH, "\p^1");
  624.         DisposeHandle(tempStringH);
  625.  
  626.         Size handleSize = GetHandleSize(templateStringH);
  627.         THROW_IF_ERROR(MemError());
  628.         if (handleSize > UCHAR_MAX)
  629.             handleSize = UCHAR_MAX;
  630.         templateString[0] = handleSize;
  631.         ODBlockMove(*templateStringH, templateString + 1, handleSize);
  632.         DisposeHandle(templateStringH);
  633.         ReportError(templateString);
  634.     CATCH_ALL
  635.     ENDTRY
  636.  
  637.     if (templateStringH)
  638.         DisposeHandle(templateStringH);
  639. }
  640.  
  641. //------------------------------------------------------------------------------
  642. //  ReportErrorGeneric
  643. //------------------------------------------------------------------------------
  644.  
  645. void ReportErrorGeneric(StringPtr errorString)
  646. {
  647.     ReportError(errorString);
  648. }
  649.  
  650. //------------------------------------------------------------------------------
  651. // ReportErrorWithNum
  652. //
  653. //    Append error number to message
  654. //------------------------------------------------------------------------------
  655.  
  656. static void ReportErrorWithNum(StringPtr errorString, OSErr error)
  657. {
  658.     Str255    errNumString;
  659.     Str255    destString;
  660.  
  661.     NumToString(error, errNumString);
  662.     CopyPascalString(destString, (StringPtr)errorString);
  663.     ConcatPascalStrings(destString, "\p: ");
  664.     ConcatPascalStrings(destString, errNumString);
  665.     InstallNotification(destString);
  666. }
  667.  
  668. //------------------------------------------------------------------------------
  669. // ReportError
  670. //------------------------------------------------------------------------------
  671.  
  672. static void ReportError(StringPtr errorString)
  673. {
  674.     InstallNotification(errorString);
  675. }
  676.  
  677. //------------------------------------------------------------------------------
  678. //    NotifyRecord
  679. //------------------------------------------------------------------------------
  680.  
  681. class NotifyRecord
  682. {
  683.   public:
  684.     NotifyRecord(StringPtr string);
  685.  
  686.   private:
  687.     NMRec            notifyRec;
  688.     Str255            message;
  689.     static NMUPP    notifyResponse;
  690.     
  691.     static pascal void RemoveNotificationRecord(NMRec* notifyRec);
  692. };
  693.  
  694. NMUPP NotifyRecord::notifyResponse
  695.         = NewNMProc(NotifyRecord::RemoveNotificationRecord);
  696.  
  697. //------------------------------------------------------------------------------
  698. //    NotifyRecord::NotifyRecord
  699. //------------------------------------------------------------------------------
  700.  
  701. NotifyRecord::NotifyRecord(StringPtr string)
  702. {
  703.     memset(&(this->notifyRec), 0, sizeof(this->notifyRec));
  704.     this->notifyRec.qType = nmType;
  705.     this->notifyRec.nmMark = true;
  706.     this->notifyRec.nmIcon = NULL;
  707.     this->notifyRec.nmSound = NULL;
  708.     CopyPascalString(message, string);
  709.     this->notifyRec.nmStr = message;
  710.     this->notifyRec.nmResp = this->notifyResponse;
  711.     this->notifyRec.nmRefCon = (long)this;
  712.  
  713.     WASSERT(!NMInstall(&this->notifyRec));
  714.     ++gPendingNotifications;
  715. }
  716.  
  717. //------------------------------------------------------------------------------
  718. //    NotifyRecord::RemoveNotificationRecord (Notification Mgr. reponse routine)
  719. //------------------------------------------------------------------------------
  720.  
  721. pascal void NotifyRecord::RemoveNotificationRecord(NMRec* notifyRec)
  722. {
  723.     // Save this off because notifyRec isn't valid after calling NMRemove.
  724.     NotifyRecord*    myRec =  (NotifyRecord*)notifyRec->nmRefCon;
  725.  
  726.     WASSERT(!NMRemove(notifyRec));
  727.     delete myRec; // do this after since notifyRec is a pointer into myRec.
  728.     --gPendingNotifications;
  729. }
  730.  
  731. //------------------------------------------------------------------------------
  732. //    InstallNotification
  733. //------------------------------------------------------------------------------
  734.  
  735. pascal void InstallNotification(StringPtr errorString)
  736. {
  737.     NotifyRecord* nRec = new NotifyRecord(errorString);
  738. }
  739.  
  740. //------------------------------------------------------------------------------
  741. //  DocumentLaunched
  742. //
  743. //    We probably want to record that we are waiting for a launch failure apple
  744. //    event so that we don't quit before we receive it.
  745. //------------------------------------------------------------------------------
  746.  
  747. void DocumentLaunched()
  748. {
  749.     // Give time for error to come back
  750.     gQuitTime = TickCount() + kWaitForErrorDelay;
  751. }
  752.  
  753. //------------------------------------------------------------------------------
  754. //    GetOurCreator
  755. //------------------------------------------------------------------------------
  756.  
  757. static OSType GetOurCreator()
  758. {
  759.     TempPlatformFile    file = new PlatformFile;
  760.     
  761.     file->Specify(&gFileSpec);
  762.     
  763.     return file->GetPlatformCreator();
  764. }
  765.  
  766. //------------------------------------------------------------------------------
  767. //    ChangeOurCreator
  768. //------------------------------------------------------------------------------
  769.  
  770. static void ChangeOurCreator(OSType type)
  771. {
  772.     TRY
  773.         TempPlatformFile    file = new PlatformFile;
  774.         
  775.         file->Specify(&gFileSpec);
  776.         
  777.         file->SetPlatformCreator(type);
  778.     CATCH_ALL
  779.     ENDTRY
  780. }
  781.  
  782. #if !GENERATINGCFM
  783. //------------------------------------------------------------------------------
  784. //    CFMIsPresent
  785. //------------------------------------------------------------------------------
  786.  
  787. ODBoolean CFMIsPresent()
  788. {
  789.     long        gestaltResponse;
  790.     ODBoolean    answer = kODTrue;
  791.  
  792.     OSErr error = Gestalt(gestaltCFMAttr, &gestaltResponse);
  793.     if (error != noErr || !(gestaltResponse & (1<<gestaltCFMPresent)))
  794.     {
  795.         ReportErrorWithNum("\pCFM must be installed to use OpenDoc."
  796.                             " Try reinstalling OpenDoc.", error);
  797.         answer = kODFalse;
  798.     }
  799.     
  800.     return answer;
  801. }
  802. #endif
  803. //------------------------------------------------------------------------------
  804. //    ReenableLauncher
  805. //------------------------------------------------------------------------------
  806.  
  807. OSErr ReenableLauncher()
  808. {
  809.     FSSpec        fileSpec;
  810.     OSErr        error;
  811.  
  812.     // TRY TO FIND LAUNCHER WHERE THE INSTALLER PUTS IT
  813.     CopyPascalString(fileSpec.name, *GetString(kSHLLauncherNameResID));
  814.     error = GetOpenDocLibsFolderInfo(&fileSpec.parID, &fileSpec.vRefNum);
  815.     if (!error)
  816.         error = EnableAppIfNotAlready(&fileSpec);
  817.     
  818.     if (error)
  819.     {
  820.         // MAYBE IT HAS BEEN RENAMED
  821.         error = FindODLibFileWithSignature(fileSpec.name,
  822.                                             fileSpec.vRefNum,
  823.                                             fileSpec.parID,
  824.                                             kODDisabledProcessSignature);
  825.         if (!error)
  826.             error = EnableAppIfNotAlready(&fileSpec);
  827.         
  828. //        if (error)
  829.             // DIDN'T FIND IT IN THE NORMAL PLACE, LET'S LOOK ELSEWHERE AND TRY
  830.             //    TO FIND ONE LAUNCHER THAT WE CAN ENABLE
  831. //            error = DoCatSearchesAndEnable(kODDisabledProcessSignature);
  832.     }
  833.  
  834.     return error;
  835. }
  836.  
  837. //------------------------------------------------------------------------------
  838. //    DoCatSearchesAndEnable
  839. //
  840. //    Looks for process by creator type and calls EnableAppIfNotAlready if found
  841. //------------------------------------------------------------------------------
  842.  
  843. OSErr DoCatSearchesAndEnable(OSType creator)
  844. {
  845.     ODBoolean    succeeded = kODFalse;
  846.  
  847.     ODVolatile(succeeded);
  848.  
  849.     TRY
  850.         StVolumeLoop     volumeLoop;
  851.         short             theVolume;
  852.         ODBoolean        foundAMatchHere = kODFalse;
  853.         OSErr            error;
  854.     
  855.         CSParam                    catSearchPB;
  856.         CInfoPBRec                catSearchInfoPB1;
  857.         CInfoPBRec                catSearchInfoPB2;
  858.         HParamBlockRec            volumeInfoPB;
  859.         GetVolParmsInfoBuffer    volParmsInfoBufferPB;
  860.         Ptr                        searchBuffer = NULL;
  861.         long                    searchBufferSize = 15*1024; // 15K
  862.         FSSpec                    matchedFileSpec;
  863.         Handle                    hVolumeList = NULL;
  864.         
  865.         searchBuffer = (Ptr)ODNewPtr(searchBufferSize);
  866.     
  867.         while (!succeeded && volumeLoop.NextVolume(theVolume))
  868.         {
  869.             // CHECK TO SEE IF VOLUME SUPPORTS CATSEARCH
  870.             memset(&volumeInfoPB, 0, sizeof(volumeInfoPB));
  871.             memset(&volParmsInfoBufferPB, 0, sizeof(volParmsInfoBufferPB));
  872.             volumeInfoPB.ioParam.ioVRefNum = theVolume;
  873.             volumeInfoPB.ioParam.ioBuffer = (Ptr)&volParmsInfoBufferPB;
  874.             volumeInfoPB.ioParam.ioReqCount = sizeof(volParmsInfoBufferPB);
  875.         
  876.             error = PBHGetVolParmsSync(&volumeInfoPB);
  877.             if ((error) || (volumeInfoPB.ioParam.ioReqCount !=
  878.                                             volumeInfoPB.ioParam.ioActCount))
  879.             {
  880.                 WARN("FindLauncher: Error from PBHGetVolParms.");
  881.                 continue;
  882.             }
  883.             if (!(volParmsInfoBufferPB.vMAttrib & 1<<bHasCatSearch))
  884.             {
  885.                 WARN("FindLauncher: Volume doesn't support CatSearch.");
  886.                 continue;
  887.             }
  888.     
  889.             // DO THE SEARCH
  890.             memset(&catSearchInfoPB1, 0, sizeof(catSearchInfoPB1));
  891.             memset(&catSearchInfoPB2, 0, sizeof(catSearchInfoPB2));
  892.         
  893.             catSearchInfoPB1.hFileInfo.ioNamePtr = 0; // not searching for name
  894.             catSearchInfoPB1.hFileInfo.ioFlFndrInfo.fdCreator
  895.                                                 = creator;
  896.         //    catSearchInfoPB1.hFileInfo.ioFlFndrInfo.fdType = 'MPSP';
  897.         
  898.             catSearchInfoPB2.hFileInfo.ioFlFndrInfo.fdCreator = 0xFFFFFFFF;
  899.         //    catSearchInfoPB2.hFileInfo.ioFlFndrInfo.fdType = 0xFFFFFFFF;
  900.         
  901.             memset(&catSearchPB, 0, sizeof(catSearchPB));
  902.             catSearchPB.ioMatchPtr = &matchedFileSpec;
  903.             catSearchPB.ioVRefNum = theVolume;
  904.             catSearchPB.ioSearchInfo1 = &catSearchInfoPB1;
  905.             catSearchPB.ioSearchInfo2 = &catSearchInfoPB2;
  906.             catSearchPB.ioReqMatchCount = 1;
  907.             catSearchPB.ioSearchTime = 1;
  908.             catSearchPB.ioOptBuffer = searchBuffer;
  909.             catSearchPB.ioOptBufSize = searchBufferSize;
  910.         
  911.             catSearchPB.ioSearchBits = fsSBFlFndrInfo;
  912.         
  913.             do
  914.             {
  915.                 error = PBCatSearchSync(&catSearchPB);
  916.                 if ((error != 0) && (error != eofErr))
  917.                     WARN("Error from PBCatSearch");
  918.                 if ((! error) && (catSearchPB.ioActMatchCount != 0))
  919.                 {
  920.                     error = EnableAppIfNotAlready(&matchedFileSpec);
  921.                     if (!error)
  922.                         succeeded = kODTrue;
  923.                 }
  924.             }
  925.             while (!error && !succeeded);
  926.         }
  927.         ODDisposePtr(searchBuffer);
  928.     CATCH_ALL
  929.         WARN("DoCatSearchesAndEnable: Throw detected, error: %d", ErrorCode());
  930.         succeeded = kODFalse;
  931.     ENDTRY
  932.  
  933.     return succeeded ? noErr : fnfErr;
  934. }
  935.  
  936. //------------------------------------------------------------------------------
  937. //    EnableApp
  938. //------------------------------------------------------------------------------
  939.  
  940. OSErr EnableApp(FSSpec* fileSpec)
  941. {
  942.     OSErr    result = noErr;
  943.     
  944.     ODVolatile(result);
  945.  
  946.     TRY
  947.         TempPlatformFile    file = new PlatformFile;
  948.         
  949.         file->Specify(fileSpec);
  950.  
  951.         file->SetPlatformCreator(kODShellSignature);
  952.         ChangeDesktopDatabase(fileSpec, kODAddAPPL);
  953.     CATCH_ALL
  954.         result = ErrorCode();
  955.     ENDTRY
  956.     
  957.     return result;
  958. }
  959.  
  960. //------------------------------------------------------------------------------
  961. //    EnableAppIfNotAlready
  962. //
  963. //    Don't want to hit the desktop database if we don't have to.
  964. //------------------------------------------------------------------------------
  965.  
  966. OSErr EnableAppIfNotAlready(FSSpec* fileSpec)
  967. {
  968.     OSErr    result = noErr;
  969.     OSType    type;
  970.     
  971.     ODVolatile(result);
  972.  
  973.     TRY
  974.         TempPlatformFile    file = new PlatformFile;
  975.         
  976.         file->Specify(fileSpec);
  977.  
  978.         type = file->GetPlatformCreator();
  979.         if (type != kODShellSignature)
  980.         {
  981.             file->SetPlatformCreator(kODShellSignature);
  982.             ChangeDesktopDatabase(fileSpec, kODAddAPPL);
  983.         }
  984.     CATCH_ALL
  985.         result = ErrorCode();
  986.     ENDTRY
  987.     
  988.     return result;
  989. }
  990.  
  991. //------------------------------------------------------------------------------
  992. //    DisableApp
  993. //------------------------------------------------------------------------------
  994.  
  995. void DisableApp(FSSpec* fileSpec, OSType newSignature)
  996. {
  997.     TRY
  998.         TempPlatformFile    file = new PlatformFile;
  999.         
  1000.         file->Specify(fileSpec);
  1001.         
  1002.         file->SetPlatformCreator(newSignature);
  1003.         ChangeDesktopDatabase(fileSpec, kODRemoveAPPL);
  1004.     CATCH_ALL
  1005.     ENDTRY
  1006. }
  1007.  
  1008. //------------------------------------------------------------------------------
  1009. //    DeleteOldLauncher
  1010. //
  1011. //    The launcher will rename itself to kSHLOldLauncherNameResID when the user
  1012. //    upgrades his system to 7.5.3 or greater from an earlier system. Then we
  1013. //    can feel free to delete this useless file here.
  1014. //------------------------------------------------------------------------------
  1015.  
  1016. void DeleteOldLauncher()
  1017. {
  1018.     FSSpec    fileSpec;
  1019.     OSErr    error;
  1020.  
  1021.     CopyPascalString(fileSpec.name, *GetString(kSHLOldLauncherNameResID));
  1022.     error = GetOpenDocLibsFolderInfo(&fileSpec.parID, &fileSpec.vRefNum);
  1023.     if (!error)
  1024.         error = FSpDelete(&fileSpec);
  1025. }
  1026.  
  1027. //------------------------------------------------------------------------------
  1028. //    HandleControlMessage
  1029. //
  1030. //    Handle messages from the OpenDoc Setup control panel.
  1031. //------------------------------------------------------------------------------
  1032.  
  1033. static pascal OSErr HandleControlMessage(const AppleEvent* message,
  1034.                                             AppleEvent* reply,
  1035.                                             long refCon)
  1036. {
  1037.     long        controlMessage;
  1038.     DescType    actualType;
  1039.     Size        actualSize;
  1040.     OSErr        error;
  1041.     
  1042.     error = AEGetParamPtr(message, kControlMessageKeyword, typeInteger,
  1043.                             &actualType, (Ptr)&controlMessage,
  1044.                             sizeof(controlMessage), &actualSize);
  1045.     if (!error
  1046.             && actualType != typeInteger
  1047.             && actualSize != sizeof(controlMessage))
  1048.         error = errAECorruptData;
  1049.     
  1050.     if (!error)
  1051.     {
  1052.         if (controlMessage == kRunUntilQuitEvent)
  1053.             gRunUntilQuitEvent = true;
  1054.         else if (controlMessage == kShutdownAtLastDocQuit)
  1055.             gRunUntilQuitEvent = false;
  1056.     }
  1057.  
  1058.     return error;
  1059. }
  1060.  
  1061. //------------------------------------------------------------------------------
  1062. //    CheckPrefs
  1063. //
  1064. //    Check the preferences for information about how long to stay running.
  1065. //------------------------------------------------------------------------------
  1066.  
  1067. void CheckPrefs()
  1068. {
  1069.     ODSetUpPrefs    prefs(-1, 0);
  1070.     
  1071.     gRunUntilQuitEvent = prefs.GetRunTilSysShutdown();
  1072. }
  1073.  
  1074. //------------------------------------------------------------------------------
  1075. //    CheckForCorrectName
  1076. //
  1077. //    OpenDoc™    ->    some disabled name
  1078. //    Ourselves    ->    "OpenDoc™"
  1079. //------------------------------------------------------------------------------
  1080.  
  1081. void CheckForCorrectName()
  1082. {
  1083.     FSSpec        launcherFileSpec;
  1084.     OSErr        error;
  1085.     Str255        oldLauncherName;
  1086.     Str255        newNameForSystemProcess;
  1087.  
  1088.     // GET THE "OpenDoc™" NAME AND COMPARE IT TO OURS
  1089.     CopyPascalString(newNameForSystemProcess,
  1090.                                 *GetString(kSHLLauncherNameResID));
  1091.     if (!EqualPascalStrings(gFileSpec.name, *GetString(kSHLLauncherNameResID)))
  1092.     {
  1093.         // TRY TO FIND LAUNCHER WHERE THE INSTALLER PUTS IT
  1094.         CopyPascalString(launcherFileSpec.name,
  1095.                             *GetString(kSHLLauncherNameResID));
  1096.         error = GetOpenDocLibsFolderInfo(&launcherFileSpec.parID,
  1097.                                             &launcherFileSpec.vRefNum);
  1098.         if (!error)
  1099.         {
  1100.             CopyPascalString(oldLauncherName,
  1101.                                 *GetString(kSHLOldLauncherNameResID));
  1102.             ChangeDesktopDatabase(&launcherFileSpec, kODRemoveAPPL);
  1103.             error = FSpRename(&launcherFileSpec, oldLauncherName);
  1104.             if (!error || error == fnfErr)
  1105.             {
  1106.                 ChangeDesktopDatabase(&gFileSpec, kODRemoveAPPL);
  1107.                 error = FSpRename(&gFileSpec, newNameForSystemProcess);
  1108.                 if (!error)
  1109.                     CopyPascalString(gFileSpec.name, newNameForSystemProcess);
  1110.                 ChangeDesktopDatabase(&gFileSpec, kODAddAPPL);
  1111.             }
  1112.         }
  1113.     }
  1114. }
  1115.  
  1116. //------------------------------------------------------------------------------
  1117. //    SystemIsTooOld
  1118. //------------------------------------------------------------------------------
  1119.  
  1120. ODBoolean SystemIsTooOld()
  1121. {
  1122.     Str255        errorString;
  1123.     OSErr        error;
  1124.     short        systemVersion;
  1125.     Str255        sysVersString;
  1126.     Str255        tempString;
  1127.     Str255        ourVersString;
  1128.     short        bugFixNumber;
  1129.     long        response;
  1130.     Handle        errorStringH = NULL;
  1131.     Handle        tempStringH;
  1132.     short        ignoreValue;
  1133.  
  1134.     error = Gestalt(gestaltSystemVersion, &response);
  1135.     WASSERTM(error == noErr, "Gestalt failed! What?");
  1136.     systemVersion = LoWord(response);
  1137.  
  1138.     if (systemVersion < kOD1pt2EarliestCompatibleSysVersNum)
  1139.     {
  1140.         // Need to convert a system version number to a string
  1141.         //    Convert byte 2
  1142.         NumToString((systemVersion & 0x0F00) >> 8, sysVersString);
  1143.         ConcatPascalStrings(sysVersString, "\p.");
  1144.         //    convert byte 1
  1145.         NumToString((systemVersion & 0x00F0) >> 4, tempString);
  1146.         ConcatPascalStrings(sysVersString, tempString);
  1147.         //    convert byte 0
  1148.         bugFixNumber = systemVersion & 0x000F;
  1149.         if (bugFixNumber != 0)
  1150.         {
  1151.             NumToString(bugFixNumber, tempString);
  1152.             ConcatPascalStrings(sysVersString, "\p.");
  1153.             ConcatPascalStrings(sysVersString, tempString);
  1154.         }
  1155.         // Extract version number from our vers resource
  1156.         VersRsrc** vers = (VersRsrc**) Get1Resource('vers', 2);
  1157.         if( vers )
  1158.         {
  1159.             ODBlockMove((**vers).versionStr, ourVersString,
  1160.                         ((**vers).versionStr)[0] + 1);
  1161.             ReleaseResource((Handle)vers);
  1162.         }
  1163.         else
  1164.             // Hard-coded string OK because this failure indicates a build
  1165.             //    error. Will never happen in a shipping version.
  1166.             CopyPascalString(ourVersString, "\p<Error. No 'vers' 2 resource in \"OpenDoc™\"");
  1167.  
  1168. //            ParamText("\p????", sysVersString, NULL, NULL);
  1169.  
  1170.         // Get the error string
  1171.         GetIndString(errorString, kODSysProcStringsID, 
  1172.                         kODSPStrIndexWrongVersSys);
  1173.         // Do substitutions into the error string
  1174.         TRY
  1175.             //    Convert error string to a handle
  1176.             THROW_IF_ERROR(PtrToHand(errorString + 1, &errorStringH,
  1177.                                         errorString[0]));
  1178.             // convert first sub. string to a handle and replace
  1179.             THROW_IF_ERROR(PtrToHand(ourVersString + 1, &tempStringH,
  1180.                                         ourVersString[0]));
  1181.             ignoreValue = ReplaceText(errorStringH, tempStringH, "\p^0");
  1182.             DisposeHandle(tempStringH);
  1183.             // convert second sub. string to a handle and replace
  1184.             THROW_IF_ERROR(PtrToHand(sysVersString + 1, &tempStringH,
  1185.                                         sysVersString[0]));
  1186.             ignoreValue = ReplaceText(errorStringH, tempStringH, "\p^1");
  1187.             DisposeHandle(tempStringH);
  1188.             // convert handle back to a string
  1189.             Size handleSize = GetHandleSize(errorStringH);
  1190.             THROW_IF_ERROR(MemError());
  1191.             if (handleSize > UCHAR_MAX)
  1192.                 handleSize = UCHAR_MAX;
  1193.             errorString[0] = handleSize;
  1194.             ODBlockMove(*errorStringH, errorString + 1, handleSize);
  1195.         CATCH_ALL
  1196.             // If the substitution doesn't finish, we can at least show some
  1197.             //    of the error message, so don't reraise.
  1198.         ENDTRY
  1199.  
  1200.         // cleanup
  1201.         if (errorStringH)
  1202.             DisposeHandle(errorStringH);
  1203.  
  1204.         ReportErrorGeneric(errorString);
  1205.         return kODTrue;
  1206.     }
  1207.     return kODFalse;
  1208. }
  1209.  
  1210. //------------------------------------------------------------------------------
  1211. //    HandleCloseReopenEvent
  1212. //
  1213. //    This event is sent by a document that wants to be closed and reopened
  1214. //    (probably after the user changed the memory setting).
  1215. //------------------------------------------------------------------------------
  1216.  
  1217. #define CATCHERROR(x) error = x; if (error) goto cleanup;
  1218.  
  1219. static pascal OSErr HandleCloseReopenEvent(const AppleEvent* message,
  1220.                                             AppleEvent* reply,
  1221.                                             long refCon)
  1222. {
  1223.     FSSpec*                fileSpec = (FSSpec*)NewPtrClear(sizeof(FSSpec));
  1224.     DescType            actualType = 0;
  1225.     Size                actualSize = 0;
  1226.     OSErr                error = noErr;
  1227.     AEDesc                address = NULL_DESCRIPTOR_DEFINITION;
  1228.     LaunchParamBlockRec    launchPB;
  1229.     AEDescList            docList = NULL_DESCRIPTOR_DEFINITION;
  1230.     AEDesc                aliasDesc = NULL_DESCRIPTOR_DEFINITION;
  1231.     AppleEvent            openDocumentsEvent = NULL_DESCRIPTOR_DEFINITION;
  1232.     AEDesc                launchDesc = NULL_DESCRIPTOR_DEFINITION;
  1233.     unsigned long        currentTickCount = 0;
  1234.     unsigned long        savedTickCount = 0;
  1235.     ProcessSerialNumber    iteratorPSN; // used to iterate only.
  1236.     ODBoolean            docIsRunning = kODFalse;
  1237.     ODBoolean            docIsStillRunning = kODTrue;
  1238.     ODBoolean            sameProcResult = kODFalse;
  1239.     ProcessSerialNumber* psn = kODNULL;
  1240.  
  1241.     // Get the psn of the caller
  1242.     error = AEGetParamDesc(message, 'duhh', typeProcessSerialNumber, &address);
  1243.  
  1244.     if( error != noErr )
  1245.         goto cleanup;
  1246.         
  1247.     // Get the FSSpec of the caller
  1248.     error = AEGetParamPtr(message, keyDirectObject, typeFSS, &actualType,
  1249.                                     fileSpec, sizeof(FSSpec), &actualSize);
  1250.  
  1251.     if( error != noErr )
  1252.         goto cleanup;
  1253.         
  1254.     if (actualType != typeFSS
  1255.             && actualSize != sizeof(FSSpec))
  1256.         CATCHERROR(errAECorruptData);
  1257.     
  1258.     AppleEvent        newEvent = NULL_DESCRIPTOR_DEFINITION;
  1259.     AppleEvent        newReply = NULL_DESCRIPTOR_DEFINITION;
  1260.  
  1261.     // Create and send close/quit event
  1262.     CATCHERROR(AECreateAppleEvent(kCoreEventClass, kAEQuitApplication,
  1263.                                     &address,
  1264.                                     kAutoGenerateReturnID, kAnyTransactionID,
  1265.                                     &newEvent));
  1266.     CATCHERROR(AESend(&newEvent, &newReply,
  1267.                         kAENoReply + kAECanInteract + kAECanSwitchLayer
  1268.                         + kAEDontRecord,
  1269.                         kAEHighPriority,
  1270.                         kAEDefaultTimeout, NULL, NULL));
  1271.  
  1272.     // Idle until the process goes away    
  1273.     iteratorPSN.highLongOfPSN = 0; 
  1274.     iteratorPSN.lowLongOfPSN = kNoProcess;
  1275.  
  1276.     HLock( address.dataHandle );
  1277.     psn = new ProcessSerialNumber;
  1278.     BlockMoveData( (Ptr)(*address.dataHandle), psn, sizeof(ProcessSerialNumber) );
  1279.     HUnlock(address.dataHandle);
  1280.     
  1281.     // Start timing count
  1282.     savedTickCount = TickCount();
  1283.     
  1284.     // Make sure document is still open iterating, waiting until it closes.
  1285.     while (error != procNotFound)
  1286.     {
  1287.         error = GetNextProcess(&iteratorPSN);
  1288.         if (!error)
  1289.         {
  1290.             SameProcess(psn, &iteratorPSN,&sameProcResult);
  1291.             if( sameProcResult )
  1292.                 docIsStillRunning = kODTrue;
  1293.         }
  1294.         // Only wait a certain amount of time. After that, discontinue processing event.
  1295.         currentTickCount = TickCount();
  1296.         if( currentTickCount > ( savedTickCount + kWaitNumTicks ) )
  1297.             break;
  1298.     }
  1299.     
  1300.     // Now wait for it to actually go away before sending open event below.
  1301.     if( docIsStillRunning )
  1302.     {
  1303.         EventRecord    anEvent;
  1304.  
  1305.         iteratorPSN.highLongOfPSN = 0; 
  1306.         iteratorPSN.lowLongOfPSN = kNoProcess;
  1307.         error = noErr;    
  1308.  
  1309.         while (docIsStillRunning )
  1310.         {
  1311.             docIsStillRunning = kODFalse;
  1312.             error = noErr;
  1313.             // Go through the entire process list looking for the document.
  1314.             while (error != procNotFound)
  1315.             {
  1316.                 error = GetNextProcess(&iteratorPSN);
  1317.                 if (!error)
  1318.                 {
  1319.                     SameProcess(psn, &iteratorPSN,&sameProcResult);
  1320.                     if( sameProcResult )
  1321.                         docIsStillRunning = kODTrue;
  1322.                 }
  1323.             }
  1324.             // The other process some time to handle an event.
  1325.             WaitNextEvent(everyEvent, &anEvent, 1, (RgnHandle) nil);
  1326.  
  1327.             // Only wait a certain amount of time. After that, discontinue processing event.
  1328.             currentTickCount = TickCount();
  1329.             if( currentTickCount > (savedTickCount + kWaitForDocClosedTicks ) )
  1330.             {
  1331.                 WARN("Couldn't reopen document because it didn't quit first.");
  1332.                 break;
  1333.             }
  1334.         }
  1335.  
  1336.         if( docIsStillRunning == kODFalse )
  1337.         {
  1338.             // Launch the doc
  1339.  
  1340.             launchPB.launchBlockID = extendedBlock;
  1341.             launchPB.launchEPBLength = extendedBlockLen;
  1342.             launchPB.launchControlFlags = launchContinue
  1343.                                             | launchUseMinimum
  1344.                                             | launchInhibitDaemon;
  1345.             launchPB.launchAppSpec = fileSpec;
  1346.  
  1347.             // Must pass the doc itself as the parameter to the open document event!
  1348.             CATCHERROR( NewAlias(NULL, fileSpec, (AliasHandle*)&aliasDesc.dataHandle) );
  1349.             aliasDesc.descriptorType = typeAlias;
  1350.             CATCHERROR(AECreateList(NULL, 0, false, &docList));
  1351.             CATCHERROR(AEPutDesc(&docList, 0, &aliasDesc));
  1352.             // Just need a null desc for 3rd param, so I reuse launchDesc.
  1353.             CATCHERROR(AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments, &launchDesc,
  1354.                                             kAutoGenerateReturnID, kAnyTransactionID,
  1355.                                             &openDocumentsEvent));
  1356.             CATCHERROR(AEPutParamDesc(&openDocumentsEvent, keyDirectObject, &docList));
  1357.             CATCHERROR(AECoerceDesc(&openDocumentsEvent, typeAppParameters,
  1358.                                     &launchDesc));
  1359.             /*
  1360.             This may look a little weird, since we're actually moving the event out of
  1361.             the AppParameters descriptor.  But it's necessary, the coercison to
  1362.             typeAppParameters takes the 'aevt' and puts it all in one handle, instead
  1363.             of leaving it as a AEDesc.  So, only one handle is being added to
  1364.             the launch parameter block instead of an AEDesc.
  1365.             */
  1366.             HLock(launchDesc.dataHandle);
  1367.             launchPB.launchAppParameters = (AppParametersPtr)*(launchDesc.dataHandle);
  1368.             error = LaunchApplication(&launchPB);
  1369.         }
  1370.     }
  1371. cleanup:
  1372.     AEDisposeDesc(&newEvent);
  1373.     AEDisposeDesc(&newReply);
  1374.     AEDisposeDesc(&address);
  1375.     AEDisposeDesc(&docList);
  1376.     AEDisposeDesc(&aliasDesc);
  1377.     AEDisposeDesc(&openDocumentsEvent);
  1378.     AEDisposeDesc(&launchDesc);
  1379.  
  1380.     if (error)
  1381.     {
  1382.         Str255    errorString;
  1383.         if( error == fnfErr )
  1384.         // Couldn't reopen document because the file is missing. This usually
  1385.         // happens because the user created a document from stationery, then
  1386.         // when closing the document, didn't choose to save, so OpenDoc trashed it.
  1387.             GetIndString(errorString, kODSysProcStringsID,
  1388.                      kODSPStrIndexMissingDocument);
  1389.         else
  1390.             GetIndString(errorString, kODSysProcStringsID,
  1391.                             kODSPStrIndexCouldntRelaunchDoc);
  1392.         ReportErrorWithNum(errorString, error);
  1393.     }
  1394.     
  1395.     // Don't return an error here since we have already reported it to the user.
  1396.     return noErr;
  1397. }
  1398.  
  1399.